<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Markdown Viewer</title>
	<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
    <link rel="stylesheet" href="/static/css/github-markdown.min.css">
    <link rel="stylesheet" href="/static/css/github.min.css">
	<style>
		.enhance {
			position: absolute;
			top: 10px;
			right: 10px;
			color: #fff ;
			padding: 3px 6px;
			border-radius: 5px;
			font-size: 12px ;
			background: #343541de;
			opacity: 0.2;
			justify-content:space-between;
			cursor: pointer;
		}
		.enhance :hover{
			color: #bae9a4d7;
		}
		.markdown-body pre {
			width: 100%;
			line-height: 20px;
		}

		.code-container{
			display: flex;
			position: relative;
			width: 100%;
		}
		.left-num-wrapper {
			padding-top: 16px;
			margin-bottom: 16px;
			background-color: #f6f8fa;
		}
		.num-wrapper{
			counter-reset: code-num;
			display: flex;
			flex-direction: column;
			border-right: #d1d1d1 solid thin;
			width: 40px;
		}
		.num-item{
			position: relative;
			font-size: 14px;
			color: #d1d1d1;
			line-height: 20px;
			height: 20px;
		}
		.num-item:before{
			counter-increment: code-num;
			content: counter(code-num);
			position: absolute;
			right: 10px;
		}

		code .hljs-meta .hljs-string, code .hljs-regexp, code .hljs-string {
			color: #990230;
		}
	</style>
</head>
<body>
    <div id="app">
		<Row style="position:fixed;top:0;left:0;height:100vh;width:100vw;">
			<!-- 左侧列表 -->
			<i-col :xs="{ span: 0 }" :sm="{ span: 0 }" :md="{ span: 0 }" style="height:100vh;padding-top:0 20px;border-right:1px solid #888888"></i-col>
			<!-- 内容页面 -->
			<i-col :xs="{ span: 24 }" :sm="{ span: 24 }" :md="{ span: 18 }" style="height:100vh;" >
				<div class="markdown-body" style="height:100vh;padding-top:70px;">	
					<div :ref="'note_page'" v-html="content" style="padding:10px;border-right:1px solid #888;height:100%;overflow:auto;"></div>
				</div>
			</i-col>
			<i-col :xs="{ span: 0 }" :sm="{ span: 0 }" :md="{ span: 6 }"  style="height:100vh;padding-top:0 20px;border-right:1px solid #888888;padding-top:70px;">
				<div v-if="is_rendered" style="padding:10px;height:100%;overflow:auto;">
					<Anchor show-ink :container="$refs['note_page']">
						<Anchor-Link v-for="item in toc" :href="'#'+item.id" :title="item.title" :style="item.style" />
					</Anchor>
				</div>
			</i-col>
		</Row>		
		<div style="width:100vw;position:fixed;top:0;left:0px;padding:15px 20px 0px 20px;max-height:100vh;z-index:10;background-color:#ffffff;border:1px solid #888;border-left:0;">
			<Row>
				<i-col :xs="{ span: 20 }" :sm="{ span: 20 }" :md="{ span: 24 }">
					<div>
						<i-input v-model="search_text" search  placeholder="搜索笔记吧" style="display: inline-block;width:300px;max-width:100%" @on-search="handleSearch" />
					</div>
					<div style="padding: 5px 0;">
						<div v-if="!is_searching && search_result.length > 0">
							<div style="position:relative;padding-top:10px;">
								<i-button shape="circle" @click="search_result=[];search_text='';search_message='';">清空</i-button>
							</div>
							<div style="max-height:50vh;overflow: auto;">
								<List>
									<template v-for="item in search_result">
										<list-item>
											<div>
												<div>
													<a href="#" v-html="item.title" @click="handleGetNote(item.hash, true);search_result=[];search_text='';search_message='';"></a>
													<a :href="'/note/'+item.hash" target="_blank" style="margin-left:10px;border-left:1px solid #888;padding-left:10px;">新窗口打开</a>
												</div>
												<div v-html="item.best_paras.join('\n')"></div>
											</div>
										</list-item>
									</template>
								</List>
							</div>
						</div>
						<div v-if="search_message">
							{{ search_message }}
						</div>
					</div>
				</i-col>
				<i-col :xs="{ span: 4 }" :sm="{ span: 4 }" :md="{ span: 0 }" style="position:relative;">
					<i-button icon="md-list" @click="show_catalog=!show_catalog" style="float:right;"></i-button>
				</i-col>
			</Row>
			<Drawer title="目录" :closable="false" v-model="show_catalog">
				<div v-if="is_rendered" style="height:100%;overflow:auto;">
					<a v-for="item in toc" :href="'#'+item.id" :style="item.style" @click="show_catalog=false;" style="display:block" class="ivu-anchor-link-title" >{{item.title}}</a>
				</div>
			</Drawer>
		</div>
    </div>

    <!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
	<!-- import Vue.js -->
	<script src="/static/js/vue.global.js"></script>
	<!-- import stylesheet -->
	<link rel="stylesheet" href="/static/css/viewuiplus.css">
	<!-- import View UI Plus -->
	<script src="/static/js/viewuiplus.min.js"></script>
    <script src="/static/js/highlight.min.js"></script>
    <script src="/static/js/marked.min.js"></script>
	<script src="/static/js/axios.min.js"></script>
	<script src="/static/js/md5.js"></script>
	<script>
		MathJax = {
			tex: {inlineMath: [['$', '$'], ['\\(', '\\)']]}
		};
	</script>
	<script src="/static/js/MathJax/tex-chtml.js"></script>
	<script src="/static/js/marked_extension.js"></script>
    <script>
		marked.use({
			extensions: [{ // 代码高亮渲染
				name: 'code',
				renderer(token) {
					var lang = hljs.getLanguage(token.lang) ? token.lang : 'plaintext';
					var code_block = "<pre><code class=language-"+lang+" lang="+lang+">"+hljs.highlight(token.text,{language:lang}).value+"</code></pre>";
					code_block = enhanceCodeBlock(code_block, lang);
					code_block = addlineNumber(code_block);
					return code_block;
				}
			}]
		})
		marked.setOptions({
			renderer: new marked.Renderer(),
			gfm: true,
			pedantic: false,
			breaks: true
		});

		axios.defaults.timeout = 30000;
        const app = Vue.createApp({
			data() {
				var url = new URL(document.URL);
				var temp = document.URL.split("?")[0].split("#")[0].split("/");
				var note_id = null;
				if(temp.length > 2) {
					if(temp[temp.length-2].toLowerCase() == "note" && temp[temp.length-1].trim() != "") {
						note_id = temp[temp.length-1];
					}
				}
				
				return {
					show_catalog: false,
					is_rendered: false,
					content: "",
					toc: [],
					note_id: note_id,
					base_assets_url: url.origin + "/assets",
					base_api_url: url.origin + "/api",
					current_note_id: null,
					current_unlock_note_id: null,
					notes: {},
					dirs: {},
					newest_notes: [],
					search_text: "",
					search_result: [],
					is_searching: false,
					search_message: "",
					fixed_notes: [],
					cache_page: [ // 默认最多打开10个页面，这样写是为了防止页面重新渲染，导致滚动条位置变化
						{ id: "", content: "" },
						{ id: "", content: "" }
					]
				}
			},
            computed: {
            },
            methods: {
                compileMarkdown: function(content) {
                    return marked.marked(content);
                },
				renderNote(note_id, content) {
					this.content = this.compileMarkdown(content);
					Vue.nextTick().then(() => { 
						// 等渲染完之后，建立目录
						this.is_rendered = true;
						var page_container = this.$refs["note_page"];
						page_container.scrollTop = 0;
						this.toc = getMarkdownHeading2(note_id, page_container);
						console.log(this.toc);
						// 2. 执行 mathjax 渲染
						MathJax.typeset(); // 虽然不执行此语句已经可以解析数学表达式了，但是不显示，主要是字体不加载，所以执行一下。
					})
				},
				handleClickSearchItem(id) {
					handleGetNote(id);
				},
				handleGetNote(id, is_update_url=false) {
					axios.get(this.base_api_url + "/note/" + id)
						.then(response => {
							var content = response.data.content;
							var md_path = response.data.path;
							var temp = md_path.split("/");
							var title = temp[temp.length-1].split(".")[0];
							var folderPath = "/" + md_path.substring(0, md_path.lastIndexOf('/'));
							var matches = content.match(/\[.*?\]\((.*?\.assets.+?)\)/g);
							if(matches) {
								for(var i = 0; i < matches.length; i++) {
									var m = matches[i].split("](")[1];
									content = content.replace(m, this.base_assets_url + folderPath + "/" + m);
								}
							}
							this.is_rendered = false;
							this.renderNote(id, content);
							document.title = title;
							if(is_update_url) {
								changeURL(title, "/note/"+id);
							}
						})
						.catch(error => {
							console.error(error);
						});
				},
				handleSearch() {
					var search_text = this.search_text;
					this.is_searching = true;
					this.search_message = "搜索中，等一会儿哈";
					axios.get(this.base_api_url + "/search?search_text=" + search_text)
						.then(response => {
							var list = response.data;
							for(var i = 0; i < list.length; i++) {
								var item = list[i];
								item.title = markRed(item.matched_keywords, item.title);
								item.best_paras = item.best_paras.map(i => markRed(item.matched_keywords, i));
							}
							console.log(list);
							this.search_result = list;
							this.is_searching = false;
							if(response.data.length == 0) this.search_message = "没有搜到任何结果";
							else this.search_message = "";
						})
						.catch(error => {
							console.error(error);
							this.is_searching = false;
							this.search_message = "出现了错误！";
						});
				},
				handleGetNotes() {
					axios.get(this.base_api_url + "/get_notes")
						.then(response => {
							var list = response.data;
							this.handleNotesData(list);
						})
						.catch(error => {
							console.error(error);
						});
				},
				handleNotesData(list) {
					var dic = {};
					var dirs = {};
					for(var i = 0; i < list.length; i++) {
						var item = list[i];
						var temp = item.path.split("/");
						var title = temp[temp.length - 1];
						var dir = temp.length > 1 ? temp[0] : "_";
						var note = {
							id: item.id,
							last_update_time: item.last_update_time,
							title: title,
							path: item.path,
							content: null
						};
						dic[item.id] = note;
						if(!dirs[dir]) dirs[dir] = []
						dirs[dir].push(note.id);
					}
					var num = 5;
					if(num > list.length) num = list.length;
					this.newest_notes = list.slice(0,num).map(e => e.id);
					this.notes = dic;
					this.dirs = dirs;
				},
			},
			created() {
				if(this.note_id) {
					this.handleGetNote(this.note_id);
				}
				//this.handleGetNotes();
			}
        });
		app.use(ViewUIPlus);
		app.mount("#app");

		function changeURL(new_title, new_url) {
			var stateObject = {};
			window.history.pushState(stateObject, new_title, new_url);
			document.title = new_title;
		}

		// 标记搜索内容，如果有关键字就标记为红色
		function markRed(keywords, content) {
			content = content.replace(/\s+/g, " "); // 空白字符全部改为空格，这时候里面就没有\t了
			for(var i = 0; i < keywords.length; i++) {
				var keyword = keywords[i];
				content = content.replaceAll(keyword, "\t" + keyword + "\t\t");
			}
			// 如果有连接的两个关键词，则会出现\t\t\t这种情况，合并两个关键词
			content = content.replaceAll("\t\t\t", "");
			content = htmlEncode(content); // 进行HTML编码，此时\t不会被编码
			content = content.replaceAll("\t\t", "</span>").replaceAll("\t", "<span style=\"color:red;\">");
			return content;
		}

		// 对内容进行html编码，使得content中的以html编码形式写的内容被识别为正常文字，不被转义。
		function htmlEncode(content) {
			var element = document.createElement('div');
			element.innerText = content;
			return element.innerHTML;
		}

		// 获取所有标题，返回json形式
		function getMarkdownHeading2(note_id, element) {
			var heading_styles = {
				"h1": "font-size:20px;font-weight:bold;padding-left:10px",
				"h2": "font-size:16px;font-weight:bold;padding-left:20px;",
				"h3": "font-size:14px;font-weight:bold;padding-left:30px;",
				"h4": "font-size:12px;font-weight:bold;padding-left:40px;color:#888;",
				"h5": "font-size:12px;font-weight:400;padding-left:50px;color:#888;",
				"h6": "font-size:12px;font-weight:100;padding-left:60px;color:#888;"
			};

			var headings = element.querySelectorAll('h1,h2,h3,h4,h5,h6');
			let result = [];
			headings.forEach((heading, index) => {
				var id = hex_md5(note_id+index);
				heading.id = id;
				var tagName = heading.tagName.toLowerCase();
				var title = heading.innerText;
				result.push({
					id: id,
					title: title,
					style: heading_styles[tagName]
				})
			});

			return result;
		}

		// 生成随机ID
		function generateRandomString(length) {
			return Math.random().toString(36).substr(2, length);
		}
    
		
		// 为代码添加行号
		function addlineNumber(content) {
			var len = content.split("\n").length;
			var numSpan = "<span class='num-item'></span>".repeat(len);
			var wrapper = `<div class="code-container"><div class="left-num-wrapper"><div class="num-wrapper">${numSpan}</div></div>${content}</div>`
			return wrapper;
		}

		//增强代码块，添加复制功能
		function enhanceCodeBlock(content, lang) {
			// console.log(content)
			//在pre块中增加一个元素用于显示
			let enhance = content.replace(/<pre><code/g, '<pre><div class="enhance" onclick="copyCodeToClip(this)"><div class="copy">'+lang+'</div></div><code')
			// console.log(enhance)
			return enhance;
		}

		/**
		 * 复制单行内容到粘贴板
		 * elem : 需要复制的元素
		 */
		function copyCodeToClip(elem) {
			var content = elem.parentElement.querySelector("code").innerText
			var aux = document.createElement("textarea");
			aux.value = content;
			document.body.appendChild(aux); 
			aux.select();
			document.execCommand("copy"); 
			document.body.removeChild(aux);
			app._context.components.Message.success("复制成功");
		}
	</script>
</body>
</html>
